#include "QGlobalShortcut.h"

#include "QGlobalShortcutPrivate.h"
#include <QAbstractEventDispatcher>
#include <QtDebug>

namespace kryptos { namespace util {

#ifndef Q_WS_MAC
int QGlobalShortcutPrivate::ref = 0;
QAbstractEventDispatcher::EventFilter QGlobalShortcutPrivate::prevEventFilter = 0;
#endif // Q_WS_MAC

bool QGlobalShortcutPrivate::error = false;
QHash<QPair<quint32, quint32>, QGlobalShortcut*> QGlobalShortcutPrivate::shortcuts;


QGlobalShortcutPrivate::QGlobalShortcutPrivate(QGlobalShortcut &handler)
    : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier), handler(handler)
{
#ifndef Q_WS_MAC
    if (!ref++)
        prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter);
#endif // Q_WS_MAC
}

QGlobalShortcutPrivate::~QGlobalShortcutPrivate()
{
#ifndef Q_WS_MAC
    if (!--ref)
        QAbstractEventDispatcher::instance()->setEventFilter(prevEventFilter);
#endif // Q_WS_MAC
}

bool QGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut)
{
    Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;
    key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]);
    mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods);
    const quint32 nativeKey = nativeKeycode(key);
    const quint32 nativeMods = nativeModifiers(mods);
    const bool res = registerShortcut(nativeKey, nativeMods);

    if (res)
        shortcuts.insert(qMakePair(nativeKey, nativeMods), &handler);
    else
        qWarning() << "QGlobalShortcut failed to register:" << QKeySequence(key + mods).toString();

    return res;
}

bool QGlobalShortcutPrivate::unsetShortcut()
{
    bool res = false;
    const quint32 nativeKey = nativeKeycode(key);
    const quint32 nativeMods = nativeModifiers(mods);

    if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &handler)
        res = unregisterShortcut(nativeKey, nativeMods);

    if (res)
        shortcuts.remove(qMakePair(nativeKey, nativeMods));
    else
        qWarning() << "QGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString();

    key = Qt::Key(0);
    mods = Qt::KeyboardModifiers(0);

    return res;
}

void QGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
{
    QGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
    if (shortcut && shortcut->isEnabled())
        emit shortcut->activated();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

QGlobalShortcut::QGlobalShortcut(QObject* parent)
    : QObject(parent), _p(new QGlobalShortcutPrivate(*this))
{
}

QGlobalShortcut::QGlobalShortcut(const QKeySequence& shortcut, QObject* parent)
    : QObject(parent), _p(new QGlobalShortcutPrivate(*this))
{
    setShortcut(shortcut);
}

QGlobalShortcut::~QGlobalShortcut()
{
    if (_p->key != 0)
        _p->unsetShortcut();
}

QKeySequence QGlobalShortcut::shortcut() const
{
    return QKeySequence(_p->key | _p->mods);
}

bool QGlobalShortcut::setShortcut(const QKeySequence& shortcut)
{
    if (_p->key != 0)
        _p->unsetShortcut();

    return _p->setShortcut(shortcut);
}

bool QGlobalShortcut::isEnabled() const
{
    return _p->enabled;
}

void QGlobalShortcut::setEnabled(bool enabled)
{
    _p->enabled = enabled;
}

void QGlobalShortcut::setDisabled(bool disabled)
{
    _p->enabled = !disabled;
}

}}
